#include "game_curl.h"
#include "game_macros.h"
#include "game_log.h"
#include <cstring>

CGameCurl g_CGameCurl;

BOOL CGameCurl::init(){
  if( CURLE_OK != curl_global_init(CURL_GLOBAL_ALL)){
    return FALSE;
  }
  CC_LOG("CGameCurl","Init CGameCurl with %s",curl_version());
  return TRUE;
}

struct CGameCurlGet_t{
  void* _dest;
  size_t _size;
  size_t _write;
};


static size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userp) {
  size_t write = size * nmemb;
  CGameCurl::RecvBuffer_t* buff = (CGameCurl::RecvBuffer_t*)userp;

  // CC_LOG("CGameCurl","write_callback size: %d,nmemb: %d,write: %d",size,nmemb,t->_write);



  // if(realsize + t->_write <= t->_size){
  //   memcpy(t->_dest + t->_write,ptr + t->_write,realsize);
  //   t->_write += realsize;
  // }

  // add data to the end of recvBuffer
  // write data maybe called more than once in a single request
  buff->insert(buff->end(), (char*)ptr, (char*)ptr+write);


  return write;
}

//后面再考虑完善下载时超时退出机制
static int curlProgress(void *clientp,double dltotal,double dlnow,double ultotal,double ulnow)
{
  return 0;
}

CURLcode CGameCurl::get(const char* url,RecvBuffer_t* buff){
  return get(url,buff,-1);
}

CURLcode CGameCurl::get(const char* url,RecvBuffer_t* buff,int timeout){
  CURLcode res = CURLE_FAILED_INIT;
  CURL* _curl;
  do{

    _curl = curl_easy_init();
    CC_BREAK_IF(!_curl);

    curl_easy_setopt(_curl, CURLOPT_URL,url);
    // curl_easy_setopt(_curl, CURLOPT_WRITEDATA, fp);
    // CGameCurlGet_t t = {._dest = dest,._size = size,._write = 0};
    curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION,    write_callback); // 处理 response 的回调函数
    curl_easy_setopt(_curl, CURLOPT_WRITEDATA,        buff);
    curl_easy_setopt(_curl, CURLOPT_NOPROGRESS,       0);
    curl_easy_setopt(_curl, CURLOPT_PROGRESSFUNCTION, curlProgress);
    curl_easy_setopt(_curl, CURLOPT_NOSIGNAL,         1L);
    if (timeout > 0) curl_easy_setopt(_curl, CURLOPT_TIMEOUT_MS, timeout); // Set timeout by ms
    CC_BREAK_IF(CURLE_OK != (res = curl_easy_perform(_curl)));

    if(_curl)
      curl_easy_cleanup(_curl);

    return CURLE_OK;
  }while(true);

  if(_curl)
      curl_easy_cleanup(_curl);
  return res;
}



CURLcode CGameCurl::get(const char* url,FILE* fp){
  CURLcode res = CURLE_FAILED_INIT;
  CURL* _curl;
  do{

    _curl = curl_easy_init();
    CC_BREAK_IF(!_curl);

    curl_easy_setopt(_curl, CURLOPT_URL,url);
    curl_easy_setopt(_curl, CURLOPT_WRITEDATA, fp);
    CC_BREAK_IF(CURLE_OK != (res = curl_easy_perform(_curl)));

    if(_curl)
      curl_easy_cleanup(_curl);

    return CURLE_OK;
  }while(true);

  if(_curl)
      curl_easy_cleanup(_curl);
  return res;
}

CURLcode CGameCurl::get(const char* url)
{
  CURLcode res = CURLE_FAILED_INIT;
  CURL* _curl;
  do {
    _curl = curl_easy_init();
    CC_BREAK_IF(!_curl);

    curl_easy_setopt(_curl, CURLOPT_URL,url);

    if (_writeCallback != nullptr) {
      curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, static_write_callback);
      curl_easy_setopt(_curl, CURLOPT_WRITEDATA, this);
    }
    if (_sockoptCallback != nullptr) {
      curl_easy_setopt(_curl, CURLOPT_SOCKOPTFUNCTION, static_sockopt_callback);
      curl_easy_setopt(_curl, CURLOPT_SOCKOPTDATA, this);
    }
    CC_BREAK_IF(CURLE_OK != (res = curl_easy_perform(_curl)));

    if(_curl)
      curl_easy_cleanup(_curl);

    return CURLE_OK;
  }while(true);

  if(_curl)
      curl_easy_cleanup(_curl);
  return res;
}

void CGameCurl::setSockOptCallback(std::function<sockopt_callback_t> callback)
{
  _sockoptCallback = callback;
}
void CGameCurl::setOpenSockCallback(std::function<opensock_callback_t> callback)
{
  _opensockCallback = callback;
}
void CGameCurl::setWriteCallback(std::function<write_callback_t> callback)
{
  _writeCallback = callback;
}

size_t CGameCurl::static_write_callback(void *ptr, size_t size, size_t nmemb, void *userdata)
{
  CGameCurl* This = static_cast<CGameCurl*>(userdata);
  if (This->_writeCallback != nullptr) {
    return This->_writeCallback((BYTE*)ptr, size*nmemb);
  }
  else {
    return (size_t)size*nmemb;
  }
}
curl_socket_t CGameCurl::static_opensock_callback(void *clientp, curlsocktype purpose, struct curl_sockaddr *address)
{
  CGameCurl* This = static_cast<CGameCurl*>(clientp);
  if (This->_opensockCallback != nullptr) {
    return This->_opensockCallback(purpose, address);
  }
  else {
    return CURL_SOCKET_BAD;
  }

}
int CGameCurl::static_sockopt_callback(void *clientp, curl_socket_t curlfd, curlsocktype purpose)
{
  CGameCurl* This = static_cast<CGameCurl*>(clientp);
  if (This->_sockoptCallback != nullptr) {
    return This->_sockoptCallback(curlfd, purpose);
  }
  else {
    return CURL_SOCKOPT_ERROR;
  }
}
